package framework.impl;

import framework.api.KieContainer;
import framework.api.KieRepository;
import framework.api.KieServices;
import framework.event.KieServicesEventListerner;

import java.lang.ref.WeakReference;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

import static framework.utils.ProjectClassLoader.findParentClassLoader;

public class KieServicesImpl implements KieServices {
    private volatile KieContainer classpathKContainer;
    private volatile String classpathKContainerId;
    private volatile ClassLoader classpathClassLoader;
    private final Object lock = new Object();
    private WeakReference<KieServicesEventListerner> listener;
    private final ConcurrentMap<String, KieContainer> kContainers = new ConcurrentHashMap<String, KieContainer>();

    public KieRepository getRepository() {
        return KieRepositoryImpl.INSTANCE;
    }

    @Override
    public KieContainer getKieClasspathContainer() {
        return getKieClasspathContainer( null, findParentClassLoader() );
    }

    public KieContainer getKieClasspathContainer(String containerId, ClassLoader classLoader) {
        if ( classpathKContainer == null ) {
            // these are heavy to create, don't want to end up with two
            synchronized ( lock ) {
                if ( classpathKContainer == null ) {
                    classpathClassLoader = classLoader;
                    if (containerId == null) {
                        classpathKContainerId = UUID.randomUUID().toString();
                    } else {
                        classpathKContainerId = containerId;
                    }
                    classpathKContainer = newKieClasspathContainer(classpathKContainerId, classLoader);
                } else if (classLoader != classpathClassLoader) {
                    throw new IllegalStateException("There's already another KieContainer created from a different ClassLoader");
                }
            }
        } else if (classLoader != classpathClassLoader) {
            throw new IllegalStateException("There's already another KieContainer created from a different ClassLoader");
        }

        if (containerId != null && !classpathKContainerId.equals(containerId)) {
            throw new IllegalStateException("The default global singleton KieClasspathContainer was already created with id "+classpathKContainerId);
        }

        return classpathKContainer;
    }

    public KieContainer newKieClasspathContainer(String containerId, ClassLoader classLoader) {
        if (containerId == null) {
            KieContainerImpl newContainer = new KieContainerImpl(UUID.randomUUID().toString(), new ClasspathKieProject(classLoader, listener), null);
            return newContainer;
        }
        if ( kContainers.get(containerId) == null ) {
            KieContainerImpl newContainer = new KieContainerImpl(containerId, new ClasspathKieProject(classLoader, listener), null);
            KieContainer check = kContainers.putIfAbsent(containerId, newContainer);
            if (check == null) {
                return newContainer;
            } else {
                newContainer.dispose();
                throw new IllegalStateException("There's already another KieContainer created with the id "+containerId);
            }
        } else {
            throw new IllegalStateException("There's already another KieContainer created with the id "+containerId);
        }
    }
}
